/*!
 * Cookies.js - 0.4.0
 *
 * Copyright (c) 2014, Scott Hamper
 * Licensed under the MIT license,
 * http://www.opensource.org/licenses/MIT
 */
(function(undefined) {
	'use strict';

	var Cookies = function(key, value, options) {
			return arguments.length === 1 ? Cookies.get(key) : Cookies.set(key, value, options);
		};

	// Allows for setter injection in unit tests
	Cookies._document = document;
	Cookies._navigator = navigator;

	Cookies.defaults = {
		path: '/'
	};

	Cookies.get = function(key) {
		if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) {
			Cookies._renewCache();
		}

		return Cookies._cache[key];
	};

	Cookies.set = function(key, value, options) {
		options = Cookies._getExtendedOptions(options);
		options.expires = Cookies._getExpiresDate(value === undefined ? -1 : options.expires);

		Cookies._document.cookie = Cookies._generateCookieString(key, value, options);

		return Cookies;
	};

	Cookies.expire = function(key, options) {
		return Cookies.set(key, undefined, options);
	};

	Cookies._getExtendedOptions = function(options) {
		return {
			path: options && options.path || Cookies.defaults.path,
			domain: options && options.domain || Cookies.defaults.domain,
			expires: options && options.expires || Cookies.defaults.expires,
			secure: options && options.secure !== undefined ? options.secure : Cookies.defaults.secure
		};
	};

	Cookies._isValidDate = function(date) {
		return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime());
	};

	Cookies._getExpiresDate = function(expires, now) {
		now = now || new Date();
		switch (typeof expires) {
		case 'number':
			expires = new Date(now.getTime() + expires * 1000);
			break;
		case 'string':
			expires = new Date(expires);
			break;
		}

		if (expires && !Cookies._isValidDate(expires)) {
			throw new Error('`expires` parameter cannot be converted to a valid Date instance');
		}

		return expires;
	};

	Cookies._generateCookieString = function(key, value, options) {
		key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent);
		key = key.replace(/\(/g, '%28').replace(/\)/g, '%29');
		value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent);
		options = options || {};

		var cookieString = key + '=' + value;
		cookieString += options.path ? ';path=' + options.path : '';
		cookieString += options.domain ? ';domain=' + options.domain : '';
		cookieString += options.expires ? ';expires=' + options.expires.toUTCString() : '';
		cookieString += options.secure ? ';secure' : '';

		return cookieString;
	};

	Cookies._getCookieObjectFromString = function(documentCookie) {
		var cookieObject = {};
		var cookiesArray = documentCookie ? documentCookie.split('; ') : [];

		for (var i = 0; i < cookiesArray.length; i++) {
			var cookieKvp = Cookies._getKeyValuePairFromCookieString(cookiesArray[i]);

			if (cookieObject[cookieKvp.key] === undefined) {
				cookieObject[cookieKvp.key] = cookieKvp.value;
			}
		}

		return cookieObject;
	};

	Cookies._getKeyValuePairFromCookieString = function(cookieString) {
		// "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')`
		var separatorIndex = cookieString.indexOf('=');

		// IE omits the "=" when the cookie value is an empty string
		separatorIndex = separatorIndex < 0 ? cookieString.length : separatorIndex;

		return {
			key: decodeURIComponent(cookieString.substr(0, separatorIndex)),
			value: decodeURIComponent(cookieString.substr(separatorIndex + 1))
		};
	};

	Cookies._renewCache = function() {
		Cookies._cache = Cookies._getCookieObjectFromString(Cookies._document.cookie);
		Cookies._cachedDocumentCookie = Cookies._document.cookie;
	};

	Cookies._areEnabled = function() {
		var testKey = 'cookies.js';
		var areEnabled = Cookies.set(testKey, 1).get(testKey) === '1';
		Cookies.expire(testKey);
		return areEnabled;
	};

	Cookies.enabled = Cookies._areEnabled();

	// AMD support
	if (typeof define === 'function' && define.amd) {
		define(function() {
			return Cookies;
		});
		// CommonJS and Node.js module support.
	} else if (typeof exports !== 'undefined') {
		// Support Node.js specific `module.exports` (which can be a function)
		if (typeof module !== 'undefined' && module.exports) {
			exports = module.exports = Cookies;
		}
		// But always support CommonJS module 1.1.1 spec (`exports` cannot be a function)
		exports.Cookies = Cookies;
	} else {
		window.Cookies = Cookies;
	}
})();